{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "## 视频效果：3D照片"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "## 一、前言\n",
    "最近，短视频平台火热的“希区柯杰”变焦视频获得极大关注，这种效果仿佛让普通的照片“活了过来”，具有十足的3D观感（如下面这样）。那我们也可以用几张照片达到这种效果吗？ \n",
    "\n",
    "<img style=\"display: block;\" src=\"example_3D_Photos.gif\" width = \"25%\" height = \"25%\" /></br>\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "## 二、实现思路\n",
    "3D照片可以比作成会“动”的照片，那怎么设计才能让动的不突兀呢？  \n",
    "\n",
    "首先，对照片按照一定比例进行裁剪，并放大与原照片一致，然后慢慢还原照片，这样照片就稍微有动起来的效果；  \n",
    "\n",
    "其次，我们为了加强这种3D观感，需要对照片里的人物进行分割提取（使用PaddleHub模型  deeplabv3p_xception65_humanseg 实现人像分割），在背景缩放的同时进行透射变换；  \n",
    "\n",
    "最后，我们可以添加背景音乐营造氛围。  \n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "## 三、实现过程（最后有完整代码）"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "### 1. 安装必要文件\n",
    "  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "!pip install paddlehub -i https://mirror.baidu.com/pypi/simple\r\n",
    "!pip install pydub\r\n",
    "!pip install ffmpeg "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "### 2. 导入必要模型和库"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "import numpy as np\r\n",
    "import os\r\n",
    "import paddlehub as hub\r\n",
    "import cv2\r\n",
    "import matplotlib.pyplot as plt\r\n",
    "from pydub import AudioSegment\r\n",
    "from moviepy.editor import *\r\n",
    "from ffmpeg import audio"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "### 3. 照片读取、裁剪和缩放\n",
    "设置照片x轴缩放0.15，y轴缩放0.1，并根据所设置的帧数，得到每次遍历缩放的高和宽  \n",
    "\n",
    "<img style=\"display: block; \" src=\"example_background.gif\" width = \"20%\" height = \"20%\" />  </br>\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "files=os.listdir(filepath)\r\n",
    "files.sort()\r\n",
    "width,height = 3024, 4032 # 预设图片宽高\r\n",
    "fps = 30 # 帧数设置越高，运行时间越长\r\n",
    "\r\n",
    "# 由于后面选取的音频时间原因，单个图片需要2秒，因此总共遍历2*fps\r\n",
    "# 读取照片，获取宽高,x轴缩放0.15，y轴缩放0.1\r\n",
    "dheight, dwidth = 0.1 * height / (2*2*fps), 0.15 * width / (2*2*fps)\r\n",
    "# 每次遍历，照片宽和高的缩放比例\r\n",
    "scale_w = width/(width+2*dwidth)\r\n",
    "scale_h = height/(height+2*dheight)\r\n",
    "# 照片裁剪初始位置\r\n",
    "begin_height0, begin_width0 = 0.05 * height, 0.075 * width # 上边界位置，左边界位置\r\n",
    "begin_height1, begin_width1 = 0.95 * height, 0.925 * width # 下边界位置，右边界位置\r\n",
    "\r\n",
    "# files中第0个文件为版本控制保存文件\r\n",
    "for fileimg in files[1:]:\r\n",
    "    img_ = cv2.imread(filepath+'/'+fileimg)\r\n",
    "    # 统一图片大小\r\n",
    "    img_ = cv2.resize(img_,(width,height))\r\n",
    "    for i in range(1,2*fps+1):\r\n",
    "        img = img_[int(begin_height0 - i*dheight) : int(begin_height1 + i*dheight), int(begin_width0 - i*dwidth) : int(begin_width1 + i*dwidth)]\r\n",
    "        # 恢复图片大小\r\n",
    "        img = cv2.resize(img,(width,height), interpolation=cv2.INTER_LINEAR)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "### 4. 定义分割函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "module = hub.Module(name=\"deeplabv3p_xception65_humanseg\")\r\n",
    "\r\n",
    "def do_seg(frame):\r\n",
    "    result = module.segmentation(images=[frame])\r\n",
    "    return result[0]['data']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "### 5. 透射变换\n",
    "根据照片设置变换所需的四个对应点，并计算得到变换矩阵 \n",
    "\n",
    "<img style=\"display: block;\" src=\"https://ai-studio-static-online.cdn.bcebos.com/fa0de7a943164fefbb24a79fee1ddff38b8b50256ad544018b5c3b7976e1d09d\" width = \"20%\" height = \"20%\" /></br>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "# 设置初始时透射变换所需4个对应点位置 pts1，但由于照片一直处于缩放状态，为让这4个点始终相对于人像的位置不变，也需要在遍历过程中对这些点位置进行处理\r\n",
    "pts1 = np.float32([[int(width/4.0/scale_w**i),int(height/8.0/scale_h**i)],\\\r\n",
    "            [int(width/4.0/scale_w**i),int(height-height/8.0/scale_h**i)],\\\r\n",
    "            [int(width-width/4.0/scale_w**i),int(height-height/8.0/scale_h**i)],\\\r\n",
    "\r\n",
    "# 设置最终透射变换所需4个对应点位置 pts2（具体为多次尝试后的主观设定，也可以根据效果按照意愿自行更改）\r\n",
    "pts2 = np.float32([[int(width/4.0/scale_w**i+width/(64*2)-width/(64.0*2*fps)*i),int(height/8.0/scale_h**i-height/(64.0*2*fps)*i)],\\\r\n",
    "            [int(width/4.0/scale_w**i+width/32-width/(16.0*2*fps)*i),int(height-height/8.0/scale_h**i)-width/64+width/(32.0*2*fps)*i],\\\r\n",
    "            [int(width-width/4.0/scale_w**i+width/(64.0*2*fps)*i),int(height-height/8.0/scale_h**i)-width/(64*2)+width/(64.0*2*fps)*i],\\\r\n",
    "            [int(width-width/4.0/scale_w**i+width/(64.0*2*fps)*i),int(height/8.0/scale_h**i)-width/(64.0*2*fps)*i]])           \r\n",
    "\r\n",
    "# 计算变换矩阵M\r\n",
    "M = cv2.getPerspectiveTransform(pts1,pts2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "### 6. 人像分割、缩放，以及背景缩放"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "seg = np.around(do_seg(img) / 255)\r\n",
    "seg_mask = np.repeat(seg[:,:,np.newaxis], 3, axis=2)\r\n",
    "front = seg_mask * img\r\n",
    "front = cv2.warpPerspective(front,M,(width, height)) # 人像透射变换\r\n",
    "seg_mask = cv2.warpPerspective(seg_mask,M,(width, height)) # 人像分割标记透射变换\r\n",
    "res_back = (1-seg_mask) * img"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "### 7. 写入视频"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "result = res_front + res_back \r\n",
    "out.write(result.astype(np.uint8))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "### 8. 音频处理，以及视频与音频合成"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "# 音频1.1倍速\r\n",
    "audio.a_speed(\"work/pretreatment_music/backgroun_music.wav\", \"1.1\", \"work/pretreatment_music/music.wav\")\r\n",
    "\r\n",
    "music = AudioSegment.from_wav('work/pretreatment_music/music.wav')\r\n",
    "clip = music[:8*1000]\r\n",
    "clip.export('music.mp3', format='mp3') \r\n",
    "\r\n",
    "# 读取视频\r\n",
    "video = VideoFileClip('result.mp4')\r\n",
    "# 读取音频\r\n",
    "audio = AudioFileClip('music.mp3')\r\n",
    "# 设置视频的音频 \r\n",
    "video = video.set_audio(audio)\r\n",
    "# 保存新的视频文件\r\n",
    "video.write_videofile('video.mp4')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "## 四、效果改进\n",
    "由于只是简单的人像透射变换以及背景缩放的组合处理，避免不了随着人像分割标记的移动，背景中浮现出原有的人像（如下图所示）  \n",
    "\n",
    "<img style=\"display: block;\" src=\"example_scale.png\" width = \"20%\" height = \"20%\" /></br>\n",
    "\n",
    "我们可以将人像变换前与变换后的标记矩阵相减，但由于标记矩阵中的数值移动后不一样，可以先将所以的非0值变为1，再相减，这样两个矩阵相减后得到的结果就是原人像浮现的位置。对于这一位置的填补，我们可以通过缩放填补位置矩阵进而得到放大后对应的部分图片，即利用需要填补位置的外围图片来进行填补。  \n",
    "\n",
    "考虑到人像分割模型可能对人像边缘处理效果不佳，我们又对人像进行适当放大，以覆盖边缘未处理部分（如下图所示）\n",
    "\n",
    "<img style=\"display: block;\" src=\"example_scale1.png\" width = \"20%\" height = \"20%\" /></br>\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "seg_mask_copy1 = np.where(seg_mask>0,1,0) # 透射变换前\r\n",
    "# seg_mask矩阵透射变换代码省略\r\n",
    "seg_mask_copy2 = np.where(seg_mask>0,1,0) # 透射变换后\r\n",
    "\r\n",
    "empty_mask = seg_mask_copy1-seg_mask_copy2 # 需填补部分\r\n",
    "empty_mask = np.where(empty_mask<=0,0,1)\r\n",
    "empty_mask = empty_mask.astype(np.uint8) \r\n",
    "\r\n",
    "background_mask = seg_mask + empty_mask # 此时的人像分割标记矩阵\r\n",
    "\r\n",
    "background_mask = cv2.resize(background_mask,None,fx=1.05,fy=1.05, interpolation=cv2.INTER_LINEAR)\r\n",
    "background_mask_h,background_mask_w = 1.05*height,1.05*width\r\n",
    "background_mask = background_mask[round(height*0.05/2):round(background_mask_h-height*0.05/2),round(width*0.05/2):round(background_mask_w-width*0.05/2)]\r\n",
    "\r\n",
    "scale_r = 1.095;scale_r_=0.095 # 需填补位置放大比例\r\n",
    "empty_mask = cv2.resize(empty_mask,None,fx=scale_r,fy=scale_r, interpolation=cv2.INTER_LINEAR)\r\n",
    "empty_back_h,empty_back_w = scale_r*height,scale_r*width\r\n",
    "\r\n",
    "# 裁剪，恢复矩阵大小\r\n",
    "empty_mask = empty_mask[round(height*scale_r_/2):round(empty_back_h-height*scale_r_/2),round(width*scale_r_/2):round(empty_back_w-width*scale_r_/2)]\r\n",
    "empty_back = empty_mask*img\r\n",
    "empty_back = cv2.resize(empty_back,None,fx=1/scale_r,fy=1/scale_r, interpolation=cv2.INTER_LINEAR)\r\n",
    "empty_back_h,empty_back_w,_ = empty_back.shape\r\n",
    "\r\n",
    "add_h,add_w = round((height-empty_back_h)/2),round((width-empty_back_w)/2)\r\n",
    "empty_back = np.hstack((np.zeros([empty_back_h,add_w,3]),empty_back))\r\n",
    "empty_back = np.hstack((empty_back,np.zeros([empty_back_h,add_w,3])))\r\n",
    "empty_back = np.vstack((np.zeros([add_h,width,3]),empty_back))\r\n",
    "empty_back = np.vstack((empty_back,np.zeros([add_h,width,3])))\r\n",
    "\r\n",
    "res_back = (1-background_mask) * img # 背景图片\r\n",
    "\r\n",
    "res_front = front + empty_back #人像图片\r\n",
    "\r\n",
    "res_front = cv2.resize(res_front,None,fx=1.05,fy=1.05, interpolation=cv2.INTER_LINEAR)\r\n",
    "res_front_h,res_front_w = 1.05*height,1.05*width\r\n",
    "res_front = res_front[round(height*0.05/2):round(res_front_h-height*0.05/2),round(width*0.05/2):round(res_front_w-width*0.05/2)]\r\n",
    "\r\n",
    "result = res_front + res_back "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "## 五、总结与展望\n",
    "本文利用PaddleHub模型实现3D照片效果，这里利用了4张照片和一个音频组合制作了一个特效视频 example_finally_video.mp4  \n",
    "\n",
    "虽然采用填补处理方法简单，但也能够达到良好的效果，不过为了更好的效果我仍然在探索。另外，在应用过程中我深深感受到PaddleHub中模型的强大。</br> \n",
    "\n",
    "最后，欢迎大家发现问题和我讨论。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "## 六、完整代码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "os.environ[\"CUDA_VISIBLE_DEVICES\"]=\"0\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "import numpy as np\r\n",
    "import os\r\n",
    "import paddlehub as hub\r\n",
    "import cv2\r\n",
    "import matplotlib.pyplot as plt\r\n",
    "from pydub import AudioSegment\r\n",
    "from moviepy.editor import *\r\n",
    "\r\n",
    "filepath = 'work/pretreatment_images'\r\n",
    "resultname = 'result.mp4'\r\n",
    "files=os.listdir(filepath)\r\n",
    "files.sort()\r\n",
    "\r\n",
    "width,height = 3024, 4032\r\n",
    "fps = 30 #帧数设置越高，运行时间越长\r\n",
    "fourcc = cv2.VideoWriter_fourcc('X','V','I','D')\r\n",
    "out = cv2.VideoWriter(resultname, fourcc, fps, (width,height))\r\n",
    "\r\n",
    "dheight, dwidth = 0.1 * height / (2*2*fps), 0.15 * width / (2*2*fps)\r\n",
    "begin_height0, begin_width0 = 0.05 * height, 0.075 * width\r\n",
    "begin_height1, begin_width1 = 0.95 * height, 0.925 * width\r\n",
    "\r\n",
    "scale_w = width/(width+2*dwidth)\r\n",
    "scale_h = height/(height+2*dheight)\r\n",
    "\r\n",
    "\r\n",
    "for fileimg in files[1:]:\r\n",
    "    img_ = cv2.imread(filepath+'/'+fileimg)\r\n",
    "    \r\n",
    "    # 读取照片，获取宽高,x轴缩放0.15，y轴缩放0.1\r\n",
    "    img_ = cv2.resize(img_,(width,height))\r\n",
    "\r\n",
    "    for i in range(1,2*fps+1):\r\n",
    "        img = img_[int(begin_height0 - i*dheight) : int(begin_height1 + i*dheight), int(begin_width0 - i*dwidth) : int(begin_width1 + i*dwidth)]\r\n",
    "        img = cv2.resize(img,(width,height), interpolation=cv2.INTER_LINEAR)\r\n",
    "        seg = np.around(do_seg(img) / 255)\r\n",
    "        seg_mask = np.repeat(seg[:,:,np.newaxis], 3, axis=2)\r\n",
    "        seg_mask_copy1 = np.where(seg_mask>0,1,0)\r\n",
    "\r\n",
    "        pts1 = np.float32([[int(width/4.0/scale_w**i),int(height/8.0/scale_h**i)],\\\r\n",
    "            [int(width/4.0/scale_w**i),int(height-height/8.0/scale_h**i)],\\\r\n",
    "            [int(width-width/4.0/scale_w**i),int(height-height/8.0/scale_h**i)],\\\r\n",
    "            [int(width-width/4.0/scale_w**i),int(height/8.0/scale_h**i)]])\r\n",
    "        pts2 = np.float32([[int(width/4.0/scale_w**i+width/(64*2)-width/(64.0*2*fps)*i),int(height/8.0/scale_h**i-height/(64.0*2*fps)*i)],\\\r\n",
    "            [int(width/4.0/scale_w**i+width/32-width/(16.0*2*fps)*i),int(height-height/8.0/scale_h**i)-width/64+width/(32.0*2*fps)*i],\\\r\n",
    "            [int(width-width/4.0/scale_w**i+width/(64.0*2*fps)*i),int(height-height/8.0/scale_h**i)-width/(64*2)+width/(64.0*2*fps)*i],\\\r\n",
    "            [int(width-width/4.0/scale_w**i+width/(64.0*2*fps)*i),int(height/8.0/scale_h**i)-width/(64.0*2*fps)*i]])\r\n",
    "        # pts2 = np.float32([[int(seg_width/4.0/scale_w**i+seg_width/(64*2)-seg_width/(64.0*2*fps)*i),int(seg_height/8.0/scale_h**i+seg_height/(64*2)-seg_height/(64.0*2*fps)*i)],\\\r\n",
    "        #     [int(seg_width/4.0/scale_w**i+seg_width/32-seg_width/(16.0*2*fps)*i),int(seg_height-seg_height/8.0/scale_h**i)-seg_width/64+seg_width/(32.0*2*fps)*i],\\\r\n",
    "        #     [int(seg_width-seg_width/4.0/scale_w**i-seg_width/128+seg_width/(64.0*2*fps)*i),int(seg_height-seg_height/8.0/scale_h**i)-seg_width/(64*2)+seg_width/(64.0*2*fps)*i],\\\r\n",
    "        #     [int(seg_width-seg_width/4.0/scale_w**i-seg_width/128+seg_width/(64.0*2*fps)*i),int(seg_height/8.0/scale_h**i)+seg_width/(64*2)-seg_width/(64.0*2*fps)*i]])\r\n",
    "\r\n",
    "        M = cv2.getPerspectiveTransform(pts1,pts2)\r\n",
    "\r\n",
    "        front = seg_mask * img\r\n",
    "\r\n",
    "        front = cv2.warpPerspective(front,M,(width, height))\r\n",
    "        seg_mask = cv2.warpPerspective(seg_mask,M,(width, height))\r\n",
    "\r\n",
    "        seg_mask_copy2 = np.where(seg_mask>0,1,0)\r\n",
    "\r\n",
    "        empty_mask = seg_mask_copy1-seg_mask_copy2\r\n",
    "        empty_mask = np.where(empty_mask<=0,0,1)\r\n",
    "        empty_mask = empty_mask.astype(np.uint8) \r\n",
    "\r\n",
    "        background_mask = seg_mask + empty_mask\r\n",
    "\r\n",
    "        background_mask = cv2.resize(background_mask,None,fx=1.05,fy=1.05, interpolation=cv2.INTER_LINEAR)\r\n",
    "        background_mask_h,background_mask_w = 1.05*height,1.05*width\r\n",
    "        background_mask = background_mask[round(height*0.05/2):round(background_mask_h-height*0.05/2),round(width*0.05/2):round(background_mask_w-width*0.05/2)]\r\n",
    "\r\n",
    "        scale_r = 1.095;scale_r_ = 0.095\r\n",
    "        empty_mask = cv2.resize(empty_mask,None,fx=scale_r,fy=scale_r, interpolation=cv2.INTER_LINEAR)\r\n",
    "        empty_back_h,empty_back_w = scale_r*height,scale_r*width\r\n",
    "\r\n",
    "        empty_mask = empty_mask[round(height*scale_r_/2):round(empty_back_h-height*scale_r_/2),round(width*scale_r_/2):round(empty_back_w-width*scale_r_/2)]\r\n",
    "        empty_back = empty_mask*img\r\n",
    "        empty_back = cv2.resize(empty_back,None,fx=1/scale_r,fy=1/scale_r, interpolation=cv2.INTER_LINEAR)\r\n",
    "        empty_back_h,empty_back_w,_ = empty_back.shape\r\n",
    "\r\n",
    "        add_h,add_w = round((height-empty_back_h)/2),round((width-empty_back_w)/2)\r\n",
    "        empty_back = np.hstack((np.zeros([empty_back_h,add_w,3]),empty_back))\r\n",
    "        empty_back = np.hstack((empty_back,np.zeros([empty_back_h,add_w,3])))\r\n",
    "        empty_back = np.vstack((np.zeros([add_h,width,3]),empty_back))\r\n",
    "        empty_back = np.vstack((empty_back,np.zeros([add_h,width,3])))\r\n",
    "\r\n",
    "        res_back = (1-background_mask) * img\r\n",
    "        \r\n",
    "        res_front = front + empty_back\r\n",
    "\r\n",
    "        res_front = cv2.resize(res_front,None,fx=1.05,fy=1.05, interpolation=cv2.INTER_LINEAR)\r\n",
    "        res_front_h,res_front_w=1.05*height,1.05*width\r\n",
    "        res_front = res_front[round(height*0.05/2):round(res_front_h-height*0.05/2),round(width*0.05/2):round(res_front_w-width*0.05/2)]\r\n",
    "\r\n",
    "        result = res_front + res_back \r\n",
    "    # result=result.astype(np.uint8) \r\n",
    "    # result = result[:,:,::-1]\r\n",
    "    # plt.scatter([pts1[0][0],pts1[1][0],pts1[2][0],pts1[3][0]],[pts1[0][1],pts1[1][1],pts1[2][1],pts1[3][1]],c='r',s=30)\r\n",
    "    # plt.scatter([pts2[0][0],pts2[1][0],pts2[2][0],pts2[3][0]],[pts2[0][1],pts2[1][1],pts2[2][1],pts2[3][1]],c='b',s=30)\r\n",
    "    # plt.imshow(result)\r\n",
    "        out.write(result.astype(np.uint8))\r\n",
    "\r\n",
    "out.release() \r\n",
    "\r\n",
    "audio.a_speed(\"work/pretreatment_music/backgroun_music.wav\", \"1.1\", \"work/pretreatment_music/music.wav\")\r\n",
    "\r\n",
    "music = AudioSegment.from_wav('work/pretreatment_music/music.wav')\r\n",
    "clip = music[:8*1000]\r\n",
    "clip.export('music.mp3', format='mp3') \r\n",
    "\r\n",
    "video = VideoFileClip('result.mp4')\r\n",
    "audio = AudioFileClip('music.mp3')\r\n",
    "video = video.set_audio(audio)\r\n",
    "video.write_videofile('video.mp4')"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "PaddlePaddle 2.1.2 (Python 3.5)",
   "language": "python",
   "name": "py35-paddle1.2.0"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
